Package com.mxgraph.swing.handler

Source Code of com.mxgraph.swing.handler.mxEdgeHandler

/**
* $Id: mxEdgeHandler.java,v 1.25 2010-06-01 10:18:45 gaudenz Exp $
* Copyright (c) 2008, Gaudenz Alder
*/
package com.mxgraph.swing.handler;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import com.mxgraph.model.mxGeometry;
import com.mxgraph.model.mxIGraphModel;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxPoint;
import com.mxgraph.view.mxCellState;
import com.mxgraph.view.mxConnectionConstraint;
import com.mxgraph.view.mxGraph;
import com.mxgraph.view.mxGraphView;

/**
*
*/
public class mxEdgeHandler extends mxCellHandler
{
  /**
   *
   */
  protected boolean cloneEnabled = true;

  /**
   *
   */
  protected Point[] p;

  /**
   *
   */
  protected transient String error;

  /**
   * Workaround for alt-key-state not correct in mouseReleased.
   */
  protected transient boolean gridEnabledEvent = false;

  /**
   * Workaround for shift-key-state not correct in mouseReleased.
   */
  protected transient boolean constrainedEvent = false;

  /**
   *
   */
  protected mxCellMarker marker = new mxCellMarker(graphComponent)
  {

    /**
     *
     */
    private static final long serialVersionUID = 8826073441093831764L;

    // Only returns edges if they are connectable and never returns
    // the edge that is currently being modified
    protected Object getCell(MouseEvent e)
    {
      mxGraph graph = graphComponent.getGraph();
      mxIGraphModel model = graph.getModel();
      Object cell = super.getCell(e);

      if (cell == mxEdgeHandler.this.state.getCell()
          || (!graph.isConnectableEdges() && model.isEdge(cell)))
      {
        cell = null;
      }

      return cell;
    }

    // Sets the highlight color according to isValidConnection
    protected boolean isValidState(mxCellState state)
    {
      mxIGraphModel model = graphComponent.getGraph().getModel();
      Object edge = mxEdgeHandler.this.state.getCell();
      boolean isSource = isSource(index);

      Object other = model.getTerminal(edge, !isSource);
      Object source = (isSource) ? state.getCell() : other;
      Object target = (isSource) ? other : state.getCell();

      error = validateConnection(source, target);

      return error == null;
    }

  };

  /**
   *
   * @param graphComponent
   * @param state
   */
  public mxEdgeHandler(mxGraphComponent graphComponent, mxCellState state)
  {
    super(graphComponent, state);
  }

  /**
   *
   */
  public void setCloneEnabled(boolean cloneEnabled)
  {
    this.cloneEnabled = cloneEnabled;
  }

  /**
   *
   */
  public boolean isCloneEnabled()
  {
    return cloneEnabled;
  }

  /**
   * No flip event is ignored.
   */
  protected boolean isIgnoredEvent(MouseEvent e)
  {
    return !isFlipEvent(e) && super.isIgnoredEvent(e);
  }

  /**
   *
   */
  protected boolean isFlipEvent(MouseEvent e)
  {
    return false;
  }

  /**
   * Returns the error message or an empty string if the connection for the
   * given source target pair is not valid. Otherwise it returns null.
   */
  public String validateConnection(Object source, Object target)
  {
    return graphComponent.getGraph().getEdgeValidationError(
        state.getCell(), source, target);
  }

  /**
   * Returns true if the current index is 0.
   */
  public boolean isSource(int index)
  {
    return index == 0;
  }

  /**
   * Returns true if the current index is the last index.
   */
  public boolean isTarget(int index)
  {
    return index == getHandleCount() - 2;
  }

  /**
   * Hides the middle handle if the edge is not bendable.
   */
  protected boolean isHandleVisible(int index)
  {
    return super.isHandleVisible(index) && (index != 1 || isCellBendable());
  }

  /**
   *
   */
  protected boolean isCellBendable()
  {
    return graphComponent.getGraph().isCellBendable(state.getCell());
  }

  /**
   *
   */
  protected Rectangle[] createHandles()
  {
    p = createPoints(state);
    Rectangle[] h = new Rectangle[p.length + 1];

    for (int i = 0; i < h.length - 1; i++)
    {
      h[i] = createHandle(p[i]);
    }

    h[p.length] = createHandle(state.getAbsoluteOffset().getPoint(),
        mxConstants.LABEL_HANDLE_SIZE);

    return h;
  }

  /**
   *
   */
  protected Color getHandleFillColor(int index)
  {
    boolean source = isSource(index);

    if (source || isTarget(index))
    {
      mxGraph graph = graphComponent.getGraph();
      Object terminal = graph.getModel().getTerminal(state.getCell(),
          source);

      if (terminal != null)
      {
        return (graphComponent.getGraph().isCellDisconnectable(state
            .getCell(), terminal, source)) ? mxConstants.CONNECT_HANDLE_FILLCOLOR
            : mxConstants.LOCKED_HANDLE_FILLCOLOR;
      }
    }

    return super.getHandleFillColor(index);
  }

  /**
   *
   * @param x
   * @param y
   * @return Returns the inde of the handle at the given location.
   */
  public int getIndexAt(int x, int y)
  {
    int index = super.getIndexAt(x, y);

    // Makes the complete label a trigger for the label handle
    if (index < 0 && handles != null && handlesVisible && isLabelMovable()
        && state.getLabelBounds().getRectangle().contains(x, y))
    {
      index = handles.length - 1;
    }

    return index;
  }

  /**
   *
   */
  protected Rectangle createHandle(Point center)
  {
    return createHandle(center, mxConstants.HANDLE_SIZE);
  }

  /**
   *
   */
  protected Rectangle createHandle(Point center, int size)
  {
    return new Rectangle(center.x - size / 2, center.y - size / 2, size,
        size);
  }

  /**
   *
   */
  protected Point[] createPoints(mxCellState s)
  {
    Point[] pts = new Point[s.getAbsolutePointCount()];

    for (int i = 0; i < pts.length; i++)
    {
      pts[i] = s.getAbsolutePoint(i).getPoint();
    }

    return pts;
  }

  /**
   *
   */
  protected JComponent createPreview()
  {
    JPanel preview = new JPanel()
    {
      /**
       *
       */
      private static final long serialVersionUID = -894546588972313020L;

      public void paint(Graphics g)
      {
        super.paint(g);

        if (!isLabel(index) && p != null)
        {
          ((Graphics2D) g).setStroke(mxConstants.PREVIEW_STROKE);

          if (isSource(index) || isTarget(index))
          {
            if (marker.hasValidState()
                || graphComponent.getGraph()
                    .isAllowDanglingEdges())
            {
              g.setColor(mxConstants.DEFAULT_VALID_COLOR);
            }
            else
            {
              g.setColor(mxConstants.DEFAULT_INVALID_COLOR);
            }
          }
          else
          {
            g.setColor(Color.BLACK);
          }

          Point origin = getLocation();
          Point last = p[0];

          for (int i = 1; i < p.length; i++)
          {
            g.drawLine(last.x - origin.x, last.y - origin.y, p[i].x
                - origin.x, p[i].y - origin.y);
            last = p[i];
          }
        }
      }
    };

    if (isLabel(index))
    {
      preview.setBorder(mxConstants.PREVIEW_BORDER);
    }

    preview.setOpaque(false);
    preview.setVisible(false);

    return preview;
  }

  /**
   *
   * @param point
   * @param gridEnabled
   * @return Returns the scaled, translated and grid-aligned point.
   */
  protected mxPoint convertPoint(mxPoint point, boolean gridEnabled)
  {
    mxGraph graph = graphComponent.getGraph();
    double scale = graph.getView().getScale();
    mxPoint trans = graph.getView().getTranslate();
    double x = point.getX() / scale - trans.getX();
    double y = point.getY() / scale - trans.getY();

    if (gridEnabled)
    {
      x = graph.snap(x);
      y = graph.snap(y);
    }

    point.setX(x - state.getOrigin().getX());
    point.setY(y - state.getOrigin().getY());

    return point;
  }

  /**
   *
   * @return Returns the bounds of the preview.
   */
  protected Rectangle getPreviewBounds()
  {
    Rectangle bounds = null;

    if (isLabel(index))
    {
      bounds = state.getLabelBounds().getRectangle();
    }
    else
    {
      bounds = new Rectangle(p[0]);

      for (int i = 0; i < p.length; i++)
      {
        bounds.add(p[i]);
      }

      bounds.height += 1;
      bounds.width += 1;
    }

    return bounds;
  }

  /**
   *
   */
  public void mousePressed(MouseEvent e)
  {
    super.mousePressed(e);

    if (isSource(index) || isTarget(index))
    {
      mxGraph graph = graphComponent.getGraph();
      mxIGraphModel model = graph.getModel();
      Object terminal = model.getTerminal(state.getCell(),
          isSource(index));

      if (!graph.isCellDisconnectable(state.getCell(), terminal,
          isSource(index)))
      {
        first = null;
      }
    }
  }

  /**
   *
   */
  public void mouseDragged(MouseEvent e)
  {
    if (!e.isConsumed() && first != null)
    {
      gridEnabledEvent = graphComponent.isGridEnabledEvent(e);
      constrainedEvent = graphComponent.isConstrainedEvent(e);

      boolean isSource = isSource(index);
      boolean isTarget = isTarget(index);

      Object source = null;
      Object target = null;

      if (isLabel(index))
      {
        mxPoint abs = state.getAbsoluteOffset();
        double dx = abs.getX() - first.x;
        double dy = abs.getY() - first.y;

        mxPoint pt = new mxPoint(e.getPoint());

        if (gridEnabledEvent)
        {
          pt = graphComponent.snapScaledPoint(pt, dx, dy);
        }

        if (constrainedEvent)
        {
          if (Math.abs(e.getX() - first.x) > Math.abs(e.getY()
              - first.y))
          {
            pt.setY(abs.getY());
          }
          else
          {
            pt.setX(abs.getX());
          }
        }

        Rectangle rect = getPreviewBounds();
        rect.translate((int) Math.round(pt.getX() - first.x),
            (int) Math.round(pt.getY() - first.y));
        preview.setBounds(rect);
      }
      else
      {
        // Clones the cell state and updates the absolute points using
        // the current state of this handle. This is required for
        // computing the correct perimeter points and edge style.
        mxGeometry geometry = graphComponent.getGraph()
            .getCellGeometry(state.getCell());
        mxCellState clone = (mxCellState) state.clone();
        List<mxPoint> points = geometry.getPoints();
        mxGraphView view = clone.getView();

        if (isSource || isTarget)
        {
          marker.process(e);
          mxCellState currentState = marker.getValidState();

          target = view
              .getVisibleTerminal(state.getCell(), !isSource);

          if (currentState != null)
          {
            source = currentState.getCell();
          }
          else
          {
            mxPoint pt = new mxPoint(e.getPoint());

            if (gridEnabledEvent)
            {
              pt = graphComponent.snapScaledPoint(pt);
            }

            clone.setAbsoluteTerminalPoint(pt, isSource);
          }

          if (!isSource)
          {
            Object tmp = source;
            source = target;
            target = tmp;
          }
        }
        else
        {
          mxPoint point = convertPoint(new mxPoint(e.getPoint()),
              gridEnabledEvent);

          if (points == null)
          {
            points = Arrays.asList(new mxPoint[] { point });
          }
          else if (index - 1 < points.size())
          {
            points = new ArrayList<mxPoint>(points);
            points.set(index - 1, point);
          }

          source = view.getVisibleTerminal(state.getCell(), true);
          target = view.getVisibleTerminal(state.getCell(), false);
        }

        // Computes the points for the edge style and terminals
        mxCellState sourceState = view.getState(source);
        mxCellState targetState = view.getState(target);

        mxConnectionConstraint sourceConstraint = graphComponent
            .getGraph().getConnectionConstraint(clone, sourceState,
                true);
        mxConnectionConstraint targetConstraint = graphComponent
            .getGraph().getConnectionConstraint(clone, targetState,
                false);

        /* TODO: Implement mxConstraintHandler
        mxConnectionConstraint constraint = constraintHandler.currentConstraint;

        if (constraint == null)
        {
          constraint = new mxConnectionConstraint();
        }
       
        if (isSource)
        {
          sourceConstraint = constraint;
        }
        else if (isTarget)
        {
          targetConstraint = constraint;
        }
        */

        if (!isSource || sourceState != null)
        {
          view.updateFixedTerminalPoint(clone, sourceState, true,
              sourceConstraint);
        }

        if (!isTarget || targetState != null)
        {
          view.updateFixedTerminalPoint(clone, targetState, false,
              targetConstraint);
        }

        view.updatePoints(clone, points, sourceState, targetState);
        view.updateFloatingTerminalPoints(clone, sourceState,
            targetState);

        // Uses the updated points from the cloned state to draw the preview
        p = createPoints(clone);
        preview.setBounds(getPreviewBounds());
      }

      if (!preview.isVisible()
          && graphComponent.isSignificant(e.getX() - first.x, e
              .getY()
              - first.y))
      {
        preview.setVisible(true);
      }
      else if (preview.isVisible())
      {
        preview.repaint();
      }

      e.consume();
    }
  }

  /**
   *
   */
  public void mouseReleased(MouseEvent e)
  {
    mxGraph graph = graphComponent.getGraph();

    if (!e.isConsumed() && first != null)
    {
      double dx = e.getX() - first.x;
      double dy = e.getY() - first.y;

      if (graphComponent.isSignificant(dx, dy))
      {
        if (error != null)
        {
          if (error.length() > 0)
          {
            JOptionPane.showMessageDialog(graphComponent, error);
          }
        }
        else if (isLabel(index))
        {
          mxPoint abs = state.getAbsoluteOffset();
          dx = abs.getX() - first.x;
          dy = abs.getY() - first.y;

          mxPoint pt = new mxPoint(e.getPoint());

          if (gridEnabledEvent)
          {
            pt = graphComponent.snapScaledPoint(pt, dx, dy);
          }

          if (constrainedEvent)
          {
            if (Math.abs(e.getX() - first.x) > Math.abs(e.getY()
                - first.y))
            {
              pt.setY(abs.getY());
            }
            else
            {
              pt.setX(abs.getX());
            }
          }

          moveLabelTo(state, pt.getX() + dx, pt.getY() + dy);
        }
        else if (marker.hasValidState()
            && (isSource(index) || isTarget(index)))
        {
          connect(state.getCell(), marker.getValidState().getCell(),
              isSource(index), graphComponent.isCloneEvent(e)
                  && isCloneEnabled());
        }
        else if ((!isSource(index) && !isTarget(index))
            || graphComponent.getGraph().isAllowDanglingEdges())
        {
          movePoint(state.getCell(), index, convertPoint(new mxPoint(
              e.getPoint()), gridEnabledEvent));
        }

        e.consume();
      }
    }

    if (!e.isConsumed() && isFlipEvent(e))
    {
      graph.flipEdge(state.getCell());
      e.consume();
    }

    super.mouseReleased(e);
  }

  /**
   * Extends the implementation to reset the current error and marker.
   */
  public void reset()
  {
    super.reset();

    marker.reset();
    error = null;
  }

  /**
   * Moves the edges control point with the given index to the given point.
   */
  protected void movePoint(Object edge, int pointIndex, mxPoint point)
  {
    mxIGraphModel model = graphComponent.getGraph().getModel();
    mxGeometry geometry = model.getGeometry(edge);

    if (geometry != null)
    {
      model.beginUpdate();
      try
      {
        geometry = (mxGeometry) geometry.clone();

        if (isSource(index) || isTarget(index))
        {
          connect(edge, null, isSource(index), false);
          geometry.setTerminalPoint(point, isSource(index));
        }
        else
        {
          List<mxPoint> pts = geometry.getPoints();

          if (pts == null)
          {
            pts = new ArrayList<mxPoint>();
            geometry.setPoints(pts);
          }

          if (pts != null)
          {
            if (pointIndex <= pts.size())
            {
              pts.set(pointIndex - 1, point);
            }
            else if (pointIndex - 1 <= pts.size())
            {
              pts.add(pointIndex - 1, point);
            }
          }
        }

        model.setGeometry(edge, geometry);
      }
      finally
      {
        model.endUpdate();
      }
    }
  }

  /**
   * Connects the given edge to the given source or target terminal.
   *
   * @param edge
   * @param terminal
   * @param isSource
   */
  protected void connect(Object edge, Object terminal, boolean isSource,
      boolean isClone)
  {
    mxGraph graph = graphComponent.getGraph();
    mxIGraphModel model = graph.getModel();

    model.beginUpdate();
    try
    {
      if (isClone)
      {
        Object clone = graph.cloneCells(new Object[] { edge })[0];

        Object parent = model.getParent(edge);
        graph.addCells(new Object[] { clone }, parent);

        Object other = model.getTerminal(edge, !isSource);
        graph.connectCell(clone, other, !isSource);

        graph.setSelectionCell(clone);
        edge = clone;
      }

      // Passes an empty constraint to reset constraint information
      graph.connectCell(edge, terminal, isSource,
          new mxConnectionConstraint());
    }
    finally
    {
      model.endUpdate();
    }
  }

  /**
   * Moves the label to the given position.
   */
  protected void moveLabelTo(mxCellState edgeState, double x, double y)
  {
    mxGraph graph = graphComponent.getGraph();
    mxIGraphModel model = graph.getModel();
    mxGeometry geometry = model.getGeometry(state.getCell());

    if (geometry != null)
    {
      geometry = (mxGeometry) geometry.clone();

      // Resets the relative location stored inside the geometry
      mxPoint pt = graph.getView().getRelativePoint(edgeState, x, y);
      geometry.setX(pt.getX());
      geometry.setY(pt.getY());

      // Resets the offset inside the geometry to find the offset
      // from the resulting point
      double scale = graph.getView().getScale();
      geometry.setOffset(new mxPoint(0, 0));
      pt = graph.getView().getPoint(edgeState, geometry);
      geometry.setOffset(new mxPoint(Math.round((x - pt.getX()) / scale),
          Math.round((y - pt.getY()) / scale)));

      model.setGeometry(edgeState.getCell(), geometry);
    }
  }

  /**
   *
   */
  protected Cursor getCursor(MouseEvent e, int index)
  {
    Cursor cursor = null;

    if (isLabel(index))
    {
      cursor = new Cursor(Cursor.MOVE_CURSOR);
    }
    else
    {
      cursor = new Cursor(Cursor.HAND_CURSOR);
    }

    return cursor;
  }

  /**
   *
   */
  protected Color getSelectionColor()
  {
    return mxConstants.EDGE_SELECTION_COLOR;
  }

  /**
   *
   */
  protected Stroke getSelectionStroke()
  {
    return mxConstants.EDGE_SELECTION_STROKE;
  }

  /**
   *
   */
  public void paint(Graphics g)
  {
    Graphics2D g2 = (Graphics2D) g;

    Stroke stroke = g2.getStroke();
    g2.setStroke(getSelectionStroke());
    g.setColor(getSelectionColor());

    Point last = state.getAbsolutePoint(0).getPoint();

    for (int i = 1; i < state.getAbsolutePointCount(); i++)
    {
      Point current = state.getAbsolutePoint(i).getPoint();
      Line2D line = new Line2D.Float(last.x, last.y, current.x, current.y);
     
      Rectangle bounds = g2.getStroke().createStrokedShape(line).getBounds();

      if (g.hitClip(bounds.x, bounds.y, bounds.width, bounds.height))
      {
        g2.draw(line);
      }
     
      last = current;
    }

    g2.setStroke(stroke);
    super.paint(g);
  }

}
TOP

Related Classes of com.mxgraph.swing.handler.mxEdgeHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.